packages = c('rgdal', 'maptools', 'raster','spatstat', 'tmap', 'sf', 'tidyverse', 'SpatialAcc', 'tbart', 'cclust') 
for (p in packages){ 
  if(!require(p, character.only = T)){ 
    install.packages(p) } 
  library(p,character.only = T) }

Singapore Planning Subzone (MP14_SUBZONE_WEB_PL)

mpsz = st_read(dsn = "data", layer = "MP14_SUBZONE_WEB_PL")
Reading layer `MP14_SUBZONE_WEB_PL' from data source `C:\IS415\IS415-Neighbourhood-WatchDocs\data' using driver `ESRI Shapefile'
Simple feature collection with 323 features and 15 fields
geometry type:  MULTIPOLYGON
dimension:      XY
bbox:           xmin: 2667.538 ymin: 15748.72 xmax: 56396.44 ymax: 50256.33
epsg (SRID):    NA
proj4string:    +proj=tmerc +lat_0=1.366666666666667 +lon_0=103.8333333333333 +k=1 +x_0=28001.642 +y_0=38744.572 +datum=WGS84 +units=m +no_defs
mpsz <- mpsz[order(mpsz$SUBZONE_N),]

Singapore Residents by Subzone, Age Group and Sex (Gender)

population = read_csv("data/respopagsex2000to2017.csv")
Parsed with column specification:
cols(
  PA = col_character(),
  SZ = col_character(),
  AG = col_character(),
  Sex = col_character(),
  Pop = col_double(),
  Time = col_double()
)

Singapore Residents by Subzone, Age Group and Sex, June 2017 (Gender)

popage2017pre <- population %>%
  filter(Time == 2017) %>%
  filter(AG == "65_to_69" | AG == "70_to_74" | AG == "75_to_79" | AG == "80_to_84" | AG == "85_and_over") %>%
  group_by(SZ)
popage2017pre_added <- aggregate(Pop ~ SZ, popage2017pre, sum)

Total Number of Clinics in Singapore

clinics = read_csv("data/clinics_hcidirectory.csv")
Parsed with column specification:
cols(
  clinic_name = col_character(),
  address = col_character(),
  postal_code = col_character(),
  X = col_double(),
  Y = col_double(),
  LAT = col_double(),
  LONG = col_double()
)
clinics_essentials <- c("clinic_name", "address", "LAT", "LONG", "X", "Y") # remove postal code
clinics <- clinics[clinics_essentials]

Total Number of TCM Clinics in Singapore

tcm = read_csv("data/tcm_tcmboard.csv")
Parsed with column specification:
cols(
  tcm_physician_name = col_character(),
  registration_no = col_character(),
  tcm_place_name = col_character(),
  tcm_address = col_character(),
  postal_code = col_character(),
  X = col_double(),
  Y = col_double(),
  LAT = col_double(),
  LONG = col_double()
)
tcm <- tcm[!duplicated(tcm$tcm_place_name), ]
  
tcm_essentials <- c("tcm_place_name", "tcm_address", "LAT", "LONG", "X", "Y")
tcm <- tcm[tcm_essentials]
names(tcm)[names(tcm) == "tcm_place_name"] <- "clinic_name"
names(tcm)[names(tcm) == "tcm_address"] <- "address"

Combine TCM and clinics

clinics_combined <- rbind(clinics, tcm)
clinics_combined <- st_join(st_as_sf(clinics_combined, 
                                 coords = c("X", "Y"), crs = st_crs(mpsz)), mpsz)

Number of HDB blocks per planning subzone

HDB = read_csv("data/hdb_property_information.csv")
Parsed with column specification:
cols(
  blk_no_street = col_character(),
  total_dwelling_units = col_double(),
  `1room_sold` = col_double(),
  `2room_sold` = col_double(),
  `3room_sold` = col_double(),
  `4room_sold` = col_double(),
  `5room_sold` = col_double(),
  exec_sold = col_double(),
  multigen_sold = col_double(),
  studio_apartment_sold = col_double(),
  `1room_rental` = col_double(),
  `2room_rental` = col_double(),
  `3room_rental` = col_double(),
  other_room_rental = col_double(),
  postal_code = col_character(),
  X = col_double(),
  Y = col_double(),
  LAT = col_double(),
  LONG = col_double()
)
HDB_sf <- st_as_sf(HDB, coords = c("X", "Y"), crs = st_crs(mpsz))

Residents by Age Group & Type of Dwelling, Annual

popByDwelling = read_csv("data/residents-by-age-group-and-type-of-dwelling-detailed-categories-annual.csv")
Parsed with column specification:
cols(
  year = col_double(),
  level_1 = col_character(),
  level_2 = col_character(),
  level_3 = col_character(),
  value = col_double()
)

Residents by Age Group & Type of Dwelling, Annual 2017

popByDwelling2017 <- popByDwelling %>%
  filter(year == 2017) %>%
  filter(level_1 == "65-69 Years" 
         | level_1 == "85 Years & Over" 
         | level_1 == "70-74 Years" 
         | level_1 == "75-79 Years"  
         | level_1 == "80-84 Years") %>%
  group_by(level_3)
popByDwelling2017
popByDwelling2017_added <- aggregate(value ~ level_3, popByDwelling2017, sum)

No. of blocks per subzone

mpsz_HDB <- st_join(HDB_sf,mpsz)
mpsz_HDB$`1_2room_total` <- mpsz_HDB$`1room_sold` + mpsz_HDB$`2room_sold` + mpsz_HDB$`1room_rental` + mpsz_HDB$`2room_rental`
mpsz_HDB_1_2_room = aggregate(mpsz_HDB$`1_2room_total`, by=list(SUBZONE=mpsz_HDB$SUBZONE_N), FUN=sum) %>%
  rename('No_of_1_2_room' = 'x') 
mpsz_HDB$`3room_total` <- mpsz_HDB$`3room_sold` + mpsz_HDB$`3room_rental` + mpsz_HDB$`other_room_rental`
mpsz_HDB_3_room_added = aggregate(mpsz_HDB$`3room_total`, by=list(SUBZONE=mpsz_HDB$SUBZONE_N), FUN=sum) %>%
  rename('No_of_3_room' = 'x') 
mpsz_HDB$`4room_total` <- mpsz_HDB$`4room_sold`
mpsz_HDB_4_room_added = aggregate(mpsz_HDB$`4room_total`, by=list(SUBZONE=mpsz_HDB$SUBZONE_N), FUN=sum) %>%
  rename('No_of_4_room' = 'x') 
mpsz_HDB$`5room_exec_total` <- mpsz_HDB$`5room_sold` + mpsz_HDB$`exec_sold`
mpsz_HDB_5_room_exec_added = aggregate(mpsz_HDB$`5room_exec_total`, by=list(SUBZONE=mpsz_HDB$SUBZONE_N), FUN=sum) %>%
  rename('No_of_5_room_exec' = 'x')   
mpsz_HDB_added <- left_join(mpsz_HDB_1_2_room, mpsz_HDB_3_room_added, by ='SUBZONE') %>%
  left_join(., mpsz_HDB_4_room_added, by = 'SUBZONE') %>%
  left_join(., mpsz_HDB_5_room_exec_added, by = 'SUBZONE')
mpsz_HDB_added$`total_units` <- mpsz_HDB_added$`No_of_1_2_room` + mpsz_HDB_added$`No_of_3_room` + mpsz_HDB_added$`No_of_4_room` + mpsz_HDB_added$`No_of_5_room`
total_1_2_room_units <- sum(mpsz_HDB_added$No_of_1_2_room) 
total_3_room_units <- sum(mpsz_HDB_added$No_of_3_room) 
total_4_room_units <- sum(mpsz_HDB_added$No_of_4_room) 
total_5_room_exec_units <- sum(mpsz_HDB_added$No_of_5_room_exec) 

No. of elderly per block in every subzone

total_1_2_room_units <- sum(mpsz_HDB_added$No_of_1_2_room) 
total_3_room_units <- sum(mpsz_HDB_added$No_of_3_room) 
total_4_room_units <- sum(mpsz_HDB_added$No_of_4_room) 
total_5_room_units <- sum(mpsz_HDB_added$No_of_5_room) 
mpsz_HDB$No_of_Elderly_in_block_1_2 <- ifelse(mpsz_HDB$`1_2room_total` == 0, 0, mpsz_HDB$`1_2room_total`/total_1_2_room_units) * popByDwelling2017_added$value[popByDwelling2017_added$level_3=="HDB 1- And 2-Room Flats"]
mpsz_HDB$No_of_Elderly_in_block_3 <- ifelse(mpsz_HDB$`3room_total` == 0, 0, mpsz_HDB$`3room_total`/total_3_room_units) * popByDwelling2017_added$value[popByDwelling2017_added$level_3=="HDB 3-Room Flats"] 
mpsz_HDB$No_of_Elderly_in_block_4 <- ifelse(mpsz_HDB$`4room_total` == 0, 0, mpsz_HDB$`4room_total`/total_4_room_units) * popByDwelling2017_added$value[popByDwelling2017_added$level_3=="HDB 4-Room Flats"]
mpsz_HDB$No_of_Elderly_in_block_5 <- ifelse(mpsz_HDB$`5room_exec_total` == 0, 0, mpsz_HDB$`5room_exec_total`/total_5_room_exec_units) * popByDwelling2017_added$value[popByDwelling2017_added$level_3=="HDB 5-Room And Executive Flats"]
                                    
mpsz_HDB$No_of_Elderly_in_block <- mpsz_HDB$No_of_Elderly_in_block_1_2 + mpsz_HDB$No_of_Elderly_in_block_3 + mpsz_HDB$No_of_Elderly_in_block_4 + mpsz_HDB$No_of_Elderly_in_block_5
mpsz_HDB$No_of_Elderly_in_block <- round(mpsz_HDB$No_of_Elderly_in_block)
#assign capacity of 2 per clinic
clinics_combined['capacity'] <- 2
mpsz_HDB_coords <- mpsz_HDB %>% st_coordinates() 
clinics_combined_coords <- clinics_combined %>% st_coordinates()
dm <- distance(mpsz_HDB_coords, clinics_combined_coords)
acc_hansen <- data.frame(ac(mpsz_HDB$No_of_Elderly_in_block, clinics_combined$capacity, dm, power=0.01, family="Hansen"))
colnames(acc_hansen) <- "accHansen"
acc_hansen <- tbl_df(acc_hansen)
HDB_acc <- bind_cols(mpsz_HDB, acc_hansen)
tmap_mode("view")
tmap mode set to interactive viewing
tm_shape(mpsz_HDB)+
  tm_symbols(size = 0.1)+
tm_shape(HDB_acc)+
tm_bubbles(col = "accHansen",
           n=5,
           style = "quantile",
           size = 0.1,
           border.col = "black",
           border.lwd = 1)
mpsz_HDB_filtered <- mpsz_HDB[mpsz_HDB$SUBZONE_N=="ADMIRALTY", ]
clinics_combined_filtered <- clinics_combined[clinics_combined$SUBZONE_N=="ADMIRALTY", ]
clinics_combined_filtered <- na.omit(clinics_combined_filtered)
mpsz_HDB_filtered_sp <- as_Spatial(mpsz_HDB_filtered)
clinics_combined_filtered_sp <- as_Spatial(clinics_combined_filtered)
model1 <- allocations(mpsz_HDB_filtered_sp, clinics_combined_filtered_sp, p=1)
head(model1)
sum(model1$allocdist)
optimal_loc <- unique(model1$allocation)
optimal_loc <- clinics_combined_filtered_sp[optimal_loc, ]
head(optimal_loc)
optimal_loc <- mpsz_HDB_filtered_sp[optimal_loc, ]
head(optimal_loc)
star.model1 <- star.diagram(mpsz_HDB_filtered_sp, clinics_combined_filtered_sp, alloc = model1$allocation)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9VFJVRSwgbWVzc2FnZT0gRkFMU0V9DQpwYWNrYWdlcyA9IGMoJ3JnZGFsJywgJ21hcHRvb2xzJywgJ3Jhc3RlcicsJ3NwYXRzdGF0JywgJ3RtYXAnLCAnc2YnLCAndGlkeXZlcnNlJywgJ1NwYXRpYWxBY2MnLCAndGJhcnQnLCAnY2NsdXN0JykgDQpmb3IgKHAgaW4gcGFja2FnZXMpeyANCiAgaWYoIXJlcXVpcmUocCwgY2hhcmFjdGVyLm9ubHkgPSBUKSl7IA0KICAgIGluc3RhbGwucGFja2FnZXMocCkgfSANCiAgbGlicmFyeShwLGNoYXJhY3Rlci5vbmx5ID0gVCkgfQ0KYGBgDQoNClNpbmdhcG9yZSBQbGFubmluZyBTdWJ6b25lIChNUDE0X1NVQlpPTkVfV0VCX1BMKQ0KYGBge3J9DQptcHN6ID0gc3RfcmVhZChkc24gPSAiZGF0YSIsIGxheWVyID0gIk1QMTRfU1VCWk9ORV9XRUJfUEwiKQ0KbXBzeiA8LSBtcHN6W29yZGVyKG1wc3okU1VCWk9ORV9OKSxdDQpgYGANCg0KU2luZ2Fwb3JlIFJlc2lkZW50cyBieSBTdWJ6b25lLCBBZ2UgR3JvdXAgYW5kIFNleCAoR2VuZGVyKQ0KYGBge3J9DQpwb3B1bGF0aW9uID0gcmVhZF9jc3YoImRhdGEvcmVzcG9wYWdzZXgyMDAwdG8yMDE3LmNzdiIpDQoNCmBgYA0KDQpTaW5nYXBvcmUgUmVzaWRlbnRzIGJ5IFN1YnpvbmUsIEFnZSBHcm91cCBhbmQgU2V4LCBKdW5lIDIwMTcgKEdlbmRlcikNCmBgYHtyfQ0KcG9wYWdlMjAxN3ByZSA8LSBwb3B1bGF0aW9uICU+JQ0KICBmaWx0ZXIoVGltZSA9PSAyMDE3KSAlPiUNCiAgZmlsdGVyKEFHID09ICI2NV90b182OSIgfCBBRyA9PSAiNzBfdG9fNzQiIHwgQUcgPT0gIjc1X3RvXzc5IiB8IEFHID09ICI4MF90b184NCIgfCBBRyA9PSAiODVfYW5kX292ZXIiKSAlPiUNCiAgZ3JvdXBfYnkoU1opDQoNCnBvcGFnZTIwMTdwcmVfYWRkZWQgPC0gYWdncmVnYXRlKFBvcCB+IFNaLCBwb3BhZ2UyMDE3cHJlLCBzdW0pDQoNCmBgYA0KDQoNClRvdGFsIE51bWJlciBvZiBDbGluaWNzIGluIFNpbmdhcG9yZQ0KYGBge3J9DQpjbGluaWNzID0gcmVhZF9jc3YoImRhdGEvY2xpbmljc19oY2lkaXJlY3RvcnkuY3N2IikNCg0KY2xpbmljc19lc3NlbnRpYWxzIDwtIGMoImNsaW5pY19uYW1lIiwgImFkZHJlc3MiLCAiTEFUIiwgIkxPTkciLCAiWCIsICJZIikgIyByZW1vdmUgcG9zdGFsIGNvZGUNCg0KY2xpbmljcyA8LSBjbGluaWNzW2NsaW5pY3NfZXNzZW50aWFsc10NCmBgYA0KDQoNClRvdGFsIE51bWJlciBvZiBUQ00gQ2xpbmljcyBpbiBTaW5nYXBvcmUNCmBgYHtyfQ0KdGNtID0gcmVhZF9jc3YoImRhdGEvdGNtX3RjbWJvYXJkLmNzdiIpDQoNCnRjbSA8LSB0Y21bIWR1cGxpY2F0ZWQodGNtJHRjbV9wbGFjZV9uYW1lKSwgXQ0KICANCnRjbV9lc3NlbnRpYWxzIDwtIGMoInRjbV9wbGFjZV9uYW1lIiwgInRjbV9hZGRyZXNzIiwgIkxBVCIsICJMT05HIiwgIlgiLCAiWSIpDQoNCnRjbSA8LSB0Y21bdGNtX2Vzc2VudGlhbHNdDQoNCm5hbWVzKHRjbSlbbmFtZXModGNtKSA9PSAidGNtX3BsYWNlX25hbWUiXSA8LSAiY2xpbmljX25hbWUiDQpuYW1lcyh0Y20pW25hbWVzKHRjbSkgPT0gInRjbV9hZGRyZXNzIl0gPC0gImFkZHJlc3MiDQpgYGANCg0KDQpDb21iaW5lIFRDTSBhbmQgY2xpbmljcw0KYGBge3J9DQpjbGluaWNzX2NvbWJpbmVkIDwtIHJiaW5kKGNsaW5pY3MsIHRjbSkNCmNsaW5pY3NfY29tYmluZWQgPC0gc3Rfam9pbihzdF9hc19zZihjbGluaWNzX2NvbWJpbmVkLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoIlgiLCAiWSIpLCBjcnMgPSBzdF9jcnMobXBzeikpLCBtcHN6KQ0KYGBgDQoNCk51bWJlciBvZiBIREIgYmxvY2tzIHBlciBwbGFubmluZyBzdWJ6b25lDQpgYGB7cn0NCkhEQiA9IHJlYWRfY3N2KCJkYXRhL2hkYl9wcm9wZXJ0eV9pbmZvcm1hdGlvbi5jc3YiKQ0KDQpIREJfc2YgPC0gc3RfYXNfc2YoSERCLCBjb29yZHMgPSBjKCJYIiwgIlkiKSwgY3JzID0gc3RfY3JzKG1wc3opKQ0KDQpgYGANCg0KUmVzaWRlbnRzIGJ5IEFnZSBHcm91cCAmIFR5cGUgb2YgRHdlbGxpbmcsIEFubnVhbA0KYGBge3J9DQpwb3BCeUR3ZWxsaW5nID0gcmVhZF9jc3YoImRhdGEvcmVzaWRlbnRzLWJ5LWFnZS1ncm91cC1hbmQtdHlwZS1vZi1kd2VsbGluZy1kZXRhaWxlZC1jYXRlZ29yaWVzLWFubnVhbC5jc3YiKQ0KDQpgYGANCg0KUmVzaWRlbnRzIGJ5IEFnZSBHcm91cCAmIFR5cGUgb2YgRHdlbGxpbmcsIEFubnVhbCAyMDE3DQpgYGB7cn0NCnBvcEJ5RHdlbGxpbmcyMDE3IDwtIHBvcEJ5RHdlbGxpbmcgJT4lDQogIGZpbHRlcih5ZWFyID09IDIwMTcpICU+JQ0KICBmaWx0ZXIobGV2ZWxfMSA9PSAiNjUtNjkgWWVhcnMiIA0KICAgICAgICAgfCBsZXZlbF8xID09ICI4NSBZZWFycyAmIE92ZXIiIA0KICAgICAgICAgfCBsZXZlbF8xID09ICI3MC03NCBZZWFycyIgDQogICAgICAgICB8IGxldmVsXzEgPT0gIjc1LTc5IFllYXJzIiAgDQogICAgICAgICB8IGxldmVsXzEgPT0gIjgwLTg0IFllYXJzIikgJT4lDQogIGdyb3VwX2J5KGxldmVsXzMpDQoNCnBvcEJ5RHdlbGxpbmcyMDE3DQpwb3BCeUR3ZWxsaW5nMjAxN19hZGRlZCA8LSBhZ2dyZWdhdGUodmFsdWUgfiBsZXZlbF8zLCBwb3BCeUR3ZWxsaW5nMjAxNywgc3VtKQ0KDQpgYGANCg0KDQpOby4gb2YgYmxvY2tzIHBlciBzdWJ6b25lDQpgYGB7cn0NCm1wc3pfSERCIDwtIHN0X2pvaW4oSERCX3NmLG1wc3opDQoNCm1wc3pfSERCJGAxXzJyb29tX3RvdGFsYCA8LSBtcHN6X0hEQiRgMXJvb21fc29sZGAgKyBtcHN6X0hEQiRgMnJvb21fc29sZGAgKyBtcHN6X0hEQiRgMXJvb21fcmVudGFsYCArIG1wc3pfSERCJGAycm9vbV9yZW50YWxgDQoNCm1wc3pfSERCXzFfMl9yb29tID0gYWdncmVnYXRlKG1wc3pfSERCJGAxXzJyb29tX3RvdGFsYCwgYnk9bGlzdChTVUJaT05FPW1wc3pfSERCJFNVQlpPTkVfTiksIEZVTj1zdW0pICU+JQ0KICByZW5hbWUoJ05vX29mXzFfMl9yb29tJyA9ICd4JykgDQoNCm1wc3pfSERCJGAzcm9vbV90b3RhbGAgPC0gbXBzel9IREIkYDNyb29tX3NvbGRgICsgbXBzel9IREIkYDNyb29tX3JlbnRhbGAgKyBtcHN6X0hEQiRgb3RoZXJfcm9vbV9yZW50YWxgDQoNCm1wc3pfSERCXzNfcm9vbV9hZGRlZCA9IGFnZ3JlZ2F0ZShtcHN6X0hEQiRgM3Jvb21fdG90YWxgLCBieT1saXN0KFNVQlpPTkU9bXBzel9IREIkU1VCWk9ORV9OKSwgRlVOPXN1bSkgJT4lDQogIHJlbmFtZSgnTm9fb2ZfM19yb29tJyA9ICd4JykgDQoNCm1wc3pfSERCJGA0cm9vbV90b3RhbGAgPC0gbXBzel9IREIkYDRyb29tX3NvbGRgDQoNCm1wc3pfSERCXzRfcm9vbV9hZGRlZCA9IGFnZ3JlZ2F0ZShtcHN6X0hEQiRgNHJvb21fdG90YWxgLCBieT1saXN0KFNVQlpPTkU9bXBzel9IREIkU1VCWk9ORV9OKSwgRlVOPXN1bSkgJT4lDQogIHJlbmFtZSgnTm9fb2ZfNF9yb29tJyA9ICd4JykgDQoNCm1wc3pfSERCJGA1cm9vbV9leGVjX3RvdGFsYCA8LSBtcHN6X0hEQiRgNXJvb21fc29sZGAgKyBtcHN6X0hEQiRgZXhlY19zb2xkYA0KDQptcHN6X0hEQl81X3Jvb21fZXhlY19hZGRlZCA9IGFnZ3JlZ2F0ZShtcHN6X0hEQiRgNXJvb21fZXhlY190b3RhbGAsIGJ5PWxpc3QoU1VCWk9ORT1tcHN6X0hEQiRTVUJaT05FX04pLCBGVU49c3VtKSAlPiUNCiAgcmVuYW1lKCdOb19vZl81X3Jvb21fZXhlYycgPSAneCcpICAgDQoNCm1wc3pfSERCX2FkZGVkIDwtIGxlZnRfam9pbihtcHN6X0hEQl8xXzJfcm9vbSwgbXBzel9IREJfM19yb29tX2FkZGVkLCBieSA9J1NVQlpPTkUnKSAlPiUNCiAgbGVmdF9qb2luKC4sIG1wc3pfSERCXzRfcm9vbV9hZGRlZCwgYnkgPSAnU1VCWk9ORScpICU+JQ0KICBsZWZ0X2pvaW4oLiwgbXBzel9IREJfNV9yb29tX2V4ZWNfYWRkZWQsIGJ5ID0gJ1NVQlpPTkUnKQ0KDQptcHN6X0hEQl9hZGRlZCRgdG90YWxfdW5pdHNgIDwtIG1wc3pfSERCX2FkZGVkJGBOb19vZl8xXzJfcm9vbWAgKyBtcHN6X0hEQl9hZGRlZCRgTm9fb2ZfM19yb29tYCArIG1wc3pfSERCX2FkZGVkJGBOb19vZl80X3Jvb21gICsgbXBzel9IREJfYWRkZWQkYE5vX29mXzVfcm9vbWANCmBgYA0KDQpgYGB7cn0NCnRvdGFsXzFfMl9yb29tX3VuaXRzIDwtIHN1bShtcHN6X0hEQl9hZGRlZCROb19vZl8xXzJfcm9vbSkgDQp0b3RhbF8zX3Jvb21fdW5pdHMgPC0gc3VtKG1wc3pfSERCX2FkZGVkJE5vX29mXzNfcm9vbSkgDQp0b3RhbF80X3Jvb21fdW5pdHMgPC0gc3VtKG1wc3pfSERCX2FkZGVkJE5vX29mXzRfcm9vbSkgDQp0b3RhbF81X3Jvb21fZXhlY191bml0cyA8LSBzdW0obXBzel9IREJfYWRkZWQkTm9fb2ZfNV9yb29tX2V4ZWMpIA0KDQpgYGANCg0KDQpOby4gb2YgZWxkZXJseSBwZXIgYmxvY2sgaW4gZXZlcnkgc3Viem9uZQ0KYGBge3J9DQp0b3RhbF8xXzJfcm9vbV91bml0cyA8LSBzdW0obXBzel9IREJfYWRkZWQkTm9fb2ZfMV8yX3Jvb20pIA0KdG90YWxfM19yb29tX3VuaXRzIDwtIHN1bShtcHN6X0hEQl9hZGRlZCROb19vZl8zX3Jvb20pIA0KdG90YWxfNF9yb29tX3VuaXRzIDwtIHN1bShtcHN6X0hEQl9hZGRlZCROb19vZl80X3Jvb20pIA0KdG90YWxfNV9yb29tX3VuaXRzIDwtIHN1bShtcHN6X0hEQl9hZGRlZCROb19vZl81X3Jvb20pIA0KDQptcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrXzFfMiA8LSBpZmVsc2UobXBzel9IREIkYDFfMnJvb21fdG90YWxgID09IDAsIDAsIG1wc3pfSERCJGAxXzJyb29tX3RvdGFsYC90b3RhbF8xXzJfcm9vbV91bml0cykgKiBwb3BCeUR3ZWxsaW5nMjAxN19hZGRlZCR2YWx1ZVtwb3BCeUR3ZWxsaW5nMjAxN19hZGRlZCRsZXZlbF8zPT0iSERCIDEtIEFuZCAyLVJvb20gRmxhdHMiXQ0KDQptcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrXzMgPC0gaWZlbHNlKG1wc3pfSERCJGAzcm9vbV90b3RhbGAgPT0gMCwgMCwgbXBzel9IREIkYDNyb29tX3RvdGFsYC90b3RhbF8zX3Jvb21fdW5pdHMpICogcG9wQnlEd2VsbGluZzIwMTdfYWRkZWQkdmFsdWVbcG9wQnlEd2VsbGluZzIwMTdfYWRkZWQkbGV2ZWxfMz09IkhEQiAzLVJvb20gRmxhdHMiXSANCg0KbXBzel9IREIkTm9fb2ZfRWxkZXJseV9pbl9ibG9ja180IDwtIGlmZWxzZShtcHN6X0hEQiRgNHJvb21fdG90YWxgID09IDAsIDAsIG1wc3pfSERCJGA0cm9vbV90b3RhbGAvdG90YWxfNF9yb29tX3VuaXRzKSAqIHBvcEJ5RHdlbGxpbmcyMDE3X2FkZGVkJHZhbHVlW3BvcEJ5RHdlbGxpbmcyMDE3X2FkZGVkJGxldmVsXzM9PSJIREIgNC1Sb29tIEZsYXRzIl0NCg0KbXBzel9IREIkTm9fb2ZfRWxkZXJseV9pbl9ibG9ja181IDwtIGlmZWxzZShtcHN6X0hEQiRgNXJvb21fZXhlY190b3RhbGAgPT0gMCwgMCwgbXBzel9IREIkYDVyb29tX2V4ZWNfdG90YWxgL3RvdGFsXzVfcm9vbV9leGVjX3VuaXRzKSAqIHBvcEJ5RHdlbGxpbmcyMDE3X2FkZGVkJHZhbHVlW3BvcEJ5RHdlbGxpbmcyMDE3X2FkZGVkJGxldmVsXzM9PSJIREIgNS1Sb29tIEFuZCBFeGVjdXRpdmUgRmxhdHMiXQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQoNCm1wc3pfSERCJE5vX29mX0VsZGVybHlfaW5fYmxvY2sgPC0gbXBzel9IREIkTm9fb2ZfRWxkZXJseV9pbl9ibG9ja18xXzIgKyBtcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrXzMgKyBtcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrXzQgKyBtcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrXzUNCmBgYA0KDQoNCmBgYHtyfQ0KbXBzel9IREIkTm9fb2ZfRWxkZXJseV9pbl9ibG9jayA8LSByb3VuZChtcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrKQ0KYGBgDQoNCmBgYHtyfQ0KI2Fzc2lnbiBjYXBhY2l0eSBvZiAyIHBlciBjbGluaWMNCmNsaW5pY3NfY29tYmluZWRbJ2NhcGFjaXR5J10gPC0gMg0KYGBgDQoNCmBgYHtyfQ0KbXBzel9IREJfY29vcmRzIDwtIG1wc3pfSERCICU+JSBzdF9jb29yZGluYXRlcygpIA0KY2xpbmljc19jb21iaW5lZF9jb29yZHMgPC0gY2xpbmljc19jb21iaW5lZCAlPiUgc3RfY29vcmRpbmF0ZXMoKQ0KYGBgDQoNCmBgYHtyfQ0KZG0gPC0gZGlzdGFuY2UobXBzel9IREJfY29vcmRzLCBjbGluaWNzX2NvbWJpbmVkX2Nvb3JkcykNCmFjY19oYW5zZW4gPC0gZGF0YS5mcmFtZShhYyhtcHN6X0hEQiROb19vZl9FbGRlcmx5X2luX2Jsb2NrLCBjbGluaWNzX2NvbWJpbmVkJGNhcGFjaXR5LCBkbSwgcG93ZXI9MC4wMSwgZmFtaWx5PSJIYW5zZW4iKSkNCmNvbG5hbWVzKGFjY19oYW5zZW4pIDwtICJhY2NIYW5zZW4iDQphY2NfaGFuc2VuIDwtIHRibF9kZihhY2NfaGFuc2VuKQ0KSERCX2FjYyA8LSBiaW5kX2NvbHMobXBzel9IREIsIGFjY19oYW5zZW4pDQpgYGANCg0KYGBge3J9DQp0bWFwX21vZGUoInZpZXciKQ0KYGBgDQoNCmBgYHtyfQ0KdG1fc2hhcGUobXBzel9IREIpKw0KICB0bV9zeW1ib2xzKHNpemUgPSAwLjEpKw0KdG1fc2hhcGUoSERCX2FjYykrDQp0bV9idWJibGVzKGNvbCA9ICJhY2NIYW5zZW4iLA0KICAgICAgICAgICBuPTUsDQogICAgICAgICAgIHN0eWxlID0gInF1YW50aWxlIiwNCiAgICAgICAgICAgc2l6ZSA9IDAuMSwNCiAgICAgICAgICAgYm9yZGVyLmNvbCA9ICJibGFjayIsDQogICAgICAgICAgIGJvcmRlci5sd2QgPSAxKQ0KYGBgDQoNCmBgYHtyfQ0KbXBzel9IREJfZmlsdGVyZWQgPC0gbXBzel9IREJbbXBzel9IREIkU1VCWk9ORV9OPT0iQURNSVJBTFRZIiwgXQ0KY2xpbmljc19jb21iaW5lZF9maWx0ZXJlZCA8LSBjbGluaWNzX2NvbWJpbmVkW2NsaW5pY3NfY29tYmluZWQkU1VCWk9ORV9OPT0iQURNSVJBTFRZIiwgXQ0KY2xpbmljc19jb21iaW5lZF9maWx0ZXJlZCA8LSBuYS5vbWl0KGNsaW5pY3NfY29tYmluZWRfZmlsdGVyZWQpDQpgYGANCg0KYGBge3J9DQptcHN6X0hEQl9maWx0ZXJlZF9zcCA8LSBhc19TcGF0aWFsKG1wc3pfSERCX2ZpbHRlcmVkKQ0KY2xpbmljc19jb21iaW5lZF9maWx0ZXJlZF9zcCA8LSBhc19TcGF0aWFsKGNsaW5pY3NfY29tYmluZWRfZmlsdGVyZWQpDQpgYGANCg0KYGBge3J9DQptb2RlbDEgPC0gYWxsb2NhdGlvbnMobXBzel9IREJfZmlsdGVyZWRfc3AsIGNsaW5pY3NfY29tYmluZWRfZmlsdGVyZWRfc3AsIHA9MSkNCmhlYWQobW9kZWwxKQ0KYGBgDQoNCmBgYHtyfQ0Kc3VtKG1vZGVsMSRhbGxvY2Rpc3QpDQpgYGANCg0KYGBge3J9DQpvcHRpbWFsX2xvYyA8LSB1bmlxdWUobW9kZWwxJGFsbG9jYXRpb24pDQpvcHRpbWFsX2xvYyA8LSBjbGluaWNzX2NvbWJpbmVkX2ZpbHRlcmVkX3NwW29wdGltYWxfbG9jLCBdDQpoZWFkKG9wdGltYWxfbG9jKQ0KYGBgDQoNCmBgYHtyfQ0Kb3B0aW1hbF9sb2MgPC0gbXBzel9IREJfZmlsdGVyZWRfc3Bbb3B0aW1hbF9sb2MsIF0NCmhlYWQob3B0aW1hbF9sb2MpDQpgYGANCg0KYGBge3J9DQpzdGFyLm1vZGVsMSA8LSBzdGFyLmRpYWdyYW0obXBzel9IREJfZmlsdGVyZWRfc3AsIGNsaW5pY3NfY29tYmluZWRfZmlsdGVyZWRfc3AsIGFsbG9jID0gbW9kZWwxJGFsbG9jYXRpb24pDQpgYGA=